package jdk.utils;

import org.apache.commons.lang3.RandomUtils;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import yunc.User;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

/**
 * jdbc mybatis 在批量插入数据得效率对比，在没有用批处理、使用批处理、使用事务、不使用事务几种情况下来对比：
 * 一共通过三种策略，五种情况，进行大批量数据插入测试
 * 策略分别是：
 * Mybatis 轻量级框架插入（无事务）
 * 采用JDBC直接处理（开启事务、无事务）
 * 采用JDBC批处理（开启事务、无事务）
 * 测试结果表明（消耗时间）：Mybatis轻量级插入 > JDBC直接处理 >JDBC 批处理。(JDBC 批处理+事务，效率最高)
 * 结论：设计到大量单条数据的插入，使用JDBC批处理和事务混合速度最快
 *
 */
public class Jdbc_Mybatis_Efficiency_Example {


    //代码内无事务
    private long begin = 33112001;//起始id
    private long end = begin + 100000;//每次循环插入的数据量
    private String url = "jdbc:mysql://localhost:3306/bigdata?useServerPrepStmts=false&rewriteBatchedStatements=true&useUnicode=true&amp;characterEncoding=UTF-8";
    private String user = "root";
    private String password = "0203";


    /**
     * mybatis 直接插入 没有事务   利用mybatis插入一万条数据耗时：28613，即28.6秒   非常慢
     */
    @Test
    public void insertBigData2() {
        //加载Spring，以及得到PersonMapper实例对象。这里创建的时间并不对最后结果产生很大的影响
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        // PersonMapper pMapper = (Mapper) context.getBean("personMapper");
        //创建一个人实例
        User person = new User();
        //计开始时间
        long bTime = System.currentTimeMillis();
        //开始循环，循环次数500W次。
        for (int i = 0; i < 10000; i++) {
            //为person赋值
            person.setAge(i);
            person.setName("name" + i);
            person.setSex(i % 2 == 0 ? "男" : "女");
            person.setAge(RandomUtils.nextInt(0, 100));

            //执行插入语句
            //  pMapper.insert(person);
            begin++;
        }
        //计结束时间
        long eTime = System.currentTimeMillis();
        System.out.println("插入500W条数据耗时：" + (eTime - bTime));
    }


    /**
     * 无事务情况下，jdbc的效率, 在未开启事务的情况下,平均20.5S插入1W条数据
     *     成功插入1W条数据耗时：21603
     *     成功插入1W条数据耗时：20361
     *     成功插入1W条数据耗时：20494
     *  jdbc开启事务后，平均每 3.9 秒插入 一万 数据
     *    成功插入1W条数据耗时：3863
     *    成功插入1W条数据耗时：3819
     *    成功插入1W条数据耗时：3933
     *
     *  jdbc直接处理，开启事务后，效率提高5倍不止，并且这个倍数会随着数据量的增大而增大。因为在未开启事务时，更新10000条数据，就得访问数据库10000次。导致每次操作都需要操作一次数据库。
     *
     */
    @Test
    public void insertBigData3() {
        //定义连接、statement对象
        Connection conn = null;
        PreparedStatement pstm = null;
        try {
            //加载jdbc驱动
            Class.forName("com.mysql.jdbc.Driver");
            //连接mysql
            conn = DriverManager.getConnection(url, user, password);
            //将自动提交关闭
            conn.setAutoCommit(false);
            //编写sql
            String sql = "INSERT INTO person VALUES (?,?,?,?,?,?,?)";
            //预编译sql
            pstm = conn.prepareStatement(sql);
            //开始总计时
            long bTime1 = System.currentTimeMillis();

            //循环10次，每次一万数据，一共10万
            for (int i = 0; i < 10; i++) {
                //开启分段计时，计1W数据耗时
                long bTime = System.currentTimeMillis();
                //开始循环
                while (begin < end) {
                    //赋值
                    pstm.setLong(1, begin);
                    pstm.setString(2, RandomValue.getChineseName());
                    pstm.setString(3, RandomValue.name_sex);
                    pstm.setInt(4, RandomValue.getNum(1, 100));
                    pstm.setString(5, RandomValue.getEmail(4, 15));
                    pstm.setString(6, RandomValue.getTel());
                    pstm.setString(7, RandomValue.getRoad());
                    //执行sql
                    pstm.execute();
                    begin++;
                }
                //提交事务
                conn.commit();
                //边界值自增10W
                end += 10000;
                //关闭分段计时
                long eTime = System.currentTimeMillis();
                //输出
                System.out.println("成功插入1W条数据耗时：" + (eTime - bTime));
            }
            //关闭总计时
            long eTime1 = System.currentTimeMillis();
            //输出
            System.out.println("插入10W数据共耗时：" + (eTime1 - bTime1));
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e1) {
            e1.printStackTrace();
        }
    }

    /**
     * 开启批处理情况  没有开启事务的情况  使用JDBC批处理，未开启事务下，平均每 2.1 秒插入 十万 条数据
     * 开启批处理情况  开启事务的情况     使用JDBC批处理，开启事务，平均每 1.9 秒插入 十万 条数据
     */
    @Test
    public void insertBigData() {
        //定义连接、statement对象
        Connection conn = null;
        PreparedStatement pstm = null;
        try {
            //加载jdbc驱动
            Class.forName("com.mysql.jdbc.Driver");
            //连接mysql
            conn = DriverManager.getConnection(url, user, password);
            //将自动提交关闭
            // conn.setAutoCommit(false);
            //编写sql
            String sql = "INSERT INTO person VALUES (?,?,?,?,?,?,?)";
            //预编译sql
            pstm = conn.prepareStatement(sql);
            //开始总计时
            long bTime1 = System.currentTimeMillis();

            //循环10次，每次十万数据，一共1000万
            for(int i=0;i<10;i++) {

                //开启分段计时，计1W数据耗时
                long bTime = System.currentTimeMillis();
                //开始循环
                while (begin < end) {
                    //赋值
                    pstm.setLong(1, begin);
                    pstm.setString(2, RandomValue.getChineseName());
                    pstm.setString(3, RandomValue.name_sex);
                    pstm.setInt(4, RandomValue.getNum(1, 100));
                    pstm.setString(5, RandomValue.getEmail(4, 15));
                    pstm.setString(6, RandomValue.getTel());
                    pstm.setString(7, RandomValue.getRoad());
                    //添加到同一个批处理中
                    pstm.addBatch();
                    begin++;
                }
                //执行批处理
                pstm.executeBatch();
                //提交事务
                //        conn.commit();
                //边界值自增10W
                end += 100000;
                //关闭分段计时
                long eTime = System.currentTimeMillis();
                //输出
                System.out.println("成功插入10W条数据耗时："+(eTime-bTime));
            }
            //关闭总计时
            long eTime1 = System.currentTimeMillis();
            //输出
            System.out.println("插入100W数据共耗时："+(eTime1-bTime1));
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e1) {
            e1.printStackTrace();
        }
    }


}
